home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Format CD 24
/
Amiga Format AFCD24 (Feb 1998, Issue 108).iso
/
-seriously_amiga-
/
shareware
/
programming
/
c
/
vbcc
/
pasm
/
output_elf.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-01-05
|
14KB
|
450 lines
/* $VER: pasm output_elf.c V0.5 (12.10.97)
*
* This file is part of pasm, a portable PowerPC assembler.
* Copyright (c) 1997 Frank Wille
*
* pasm is freeware and part of the portable and retargetable ANSI C
* compiler vbcc, copyright (c) 1995-97 by Volker Barthelmann.
* pasm may be freely redistributed as long as no modifications are
* made and nothing is charged for it. Non-commercial usage is allowed
* without any restrictions.
* EVERY PRODUCT OR PROGRAM DERIVED DIRECTLY FROM MY SOURCE MAY NOT BE
* SOLD COMMERCIALLY WITHOUT PERMISSION FROM THE AUTHOR.
*
*
* v0.5 (12.10.97) phx
* Removed "pasm V0.x" from .comment section.
* v0.3 (10.04.97) phx
* Fixed enforcer-hit, when external references are present.
* Some vbcc-specific changes.
* Support for little endian architectures.
* v0.2 (25.03.97) phx
* Writes ELF object for 32-bit PowerPC big-endian. Either absolute
* or ELF output format may be selected. ELF is default for all
* currently supported platforms. PPCasm supports nine different
* relocation types (there are much more...).
* Compiles and works also under NetBSD/amiga (68k).
* Changed function declaration to 'new style' in all sources
* (to avoid problems with '...' for example).
* File created.
*/
#define OUTPUT_ELF_C
#include "ppcasm.h"
#include "elf.h"
#include <time.h>
struct StrTabList {
struct list l;
uint32 index;
};
struct StrTabNode {
struct node n;
char *str;
};
struct ShdrNode {
struct node n;
struct Elf32_Shdr s;
};
struct SymbolNode {
struct node n;
char *name;
struct Elf32_Sym s;
};
struct RelaNode {
struct node n;
struct Elf32_Rela r;
};
static struct Elf32_Ehdr elf_header = {
{ 0x7f,'E','L','F',ELFCLASS32,0,EV_CURRENT,0,0,0,0,0,0,0,0,0 },
ECH(ET_REL),ECH(EM_POWERPC),ECW(EV_CURRENT),0,0,0,0,
ECH(sizeof(struct Elf32_Ehdr)),
0,0,ECH(sizeof(struct Elf32_Shdr)),0,0
};
static char *output_name;
void output_elf32msb(struct GlobalVars *);
static struct ShdrNode *addShdr(struct list *);
static struct SymbolNode *addSymbol(struct list *,struct StrTabList *,char *);
static uint32 addString(struct StrTabList *,char *);
static void addRela(struct list *,uint32,uint32,uint32);
static void fw(FILE *,void *,size_t);
void output_elf32msb(struct GlobalVars *gv)
{
struct list shdrlist,symlist,relalist;
struct StrTabList shstrlist,strlist;
struct ShdrNode *shn;
struct SymbolNode *sym;
uint32 symtabidx,strtabidx,shstrtabidx,firstglobal,align1,align2;
uint32 roffset=0,soffset=sizeof(struct Elf32_Ehdr);
uint32 shdrindex=0,symindex=0;
struct Section *nextsec,*sec=(struct Section *)gv->sectionlist.first;
struct Symbol *symchain;
int i;
FILE *fp;
/* init */
output_name = gv->dest_name;
elf_header.e_ident[EI_DATA] = ELFDATA2MSB;
initlist(&shdrlist);
initlist(&symlist);
initlist(&relalist);
shstrlist.index = strlist.index = 0;
initlist(&shstrlist.l);
initlist(&strlist.l);
addString(&shstrlist,""); /* first string is always "" */
symtabidx = addString(&shstrlist,".symtab");
strtabidx = addString(&shstrlist,".strtab");
shstrtabidx = addString(&shstrlist,".shstrtab");
addShdr(&shdrlist); /* first Shdr is always zero */
addString(&strlist,"");
addSymbol(&symlist,NULL,NULL); /* first symbol table entry always empty */
/* source file name symbol */
if (gv->file) {
++symindex;
sym = addSymbol(&symlist,&strlist,gv->file);
sym->s.st_info = ELF32_ST_INFO(STB_LOCAL,STT_FILE);
sym->s.st_shndx = ECH(SHN_ABS);
}
/* generate section headers for program sections */
while (nextsec = (struct Section *)sec->n.next) {
if (!(sec->flags & SF_DISCARD) && sec->size > 0) {
sec->index = ++shdrindex;
shn = addShdr(&shdrlist);
shn->s.sh_name = ECVW(addString(&shstrlist,sec->name));
if (sec->type==ST_UDATA || (sec->flags&SF_UNINITIALIZED))
shn->s.sh_type = ECW(SHT_NOBITS);
else
shn->s.sh_type = ECW(SHT_PROGBITS);
shn->s.sh_flags = ECW(SHF_ALLOC);
if (sec->protection & SP_WRITE)
shn->s.sh_flags |= ECW(SHF_WRITE);
if (sec->protection & SP_EXEC)
shn->s.sh_flags |= ECW(SHF_EXECINSTR);
shn->s.sh_offset = ECVW(soffset);
shn->s.sh_size = ECVW(sec->size);
if (shn->s.sh_type == ECW(SHT_PROGBITS))
soffset += sec->size;
shn->s.sh_addralign = ECVW(1<<(uint32)sec->alignment);
/* add section symbol */
sym = addSymbol(&symlist,NULL,NULL);
sym->s.st_info = ELF32_ST_INFO(STB_LOCAL,STT_SECTION);
sym->s.st_shndx = ECVH((uint16)shdrindex);
++symindex;
}
sec = nextsec;
}
/* comment section */
if (gv->ident || gv->vc) {
++shdrindex;
shn = addShdr(&shdrlist);
shn->s.sh_name = ECVW(addString(&shstrlist,".comment"));
shn->s.sh_type = ECW(SHT_PROGBITS);
shn->s.sh_offset = ECVW(soffset);
shn->s.sh_size = 1; /* zero-byte */
if (gv->ident)
shn->s.sh_size += (uint32)strlen(gv->ident)+1;
if (gv->vc)
shn->s.sh_size += 12;
soffset += shn->s.sh_size;
#ifdef LITTLEENDIAN
shn->s.sh_size = l2bw(shn->s.sh_size);
#endif
shn->s.sh_addralign = ECW(1);
/* add section symbol */
sym = addSymbol(&symlist,NULL,NULL);
sym->s.st_info = ELF32_ST_INFO(STB_LOCAL,STT_SECTION);
sym->s.st_shndx = ECVH((uint16)shdrindex);
++symindex;
}
/* build symbol table */
for (i=0; i<SYMHTABSIZE; i++) { /* first, symbols with local binding */
symchain = gv->symbols[i];
while (symchain) {
if (*(symchain->name) != '.') { /* '.symbols' are ignored */
if (symchain->type==SYM_ABS || symchain->type==SYM_RELOC) {
if (symchain->bind == SYMB_LOCAL) {
++symindex;
sym = addSymbol(&symlist,&strlist,symchain->name);
sym->s.st_value = ECVW(symchain->value);
sym->s.st_size = ECVW(symchain->size);
sym->s.st_info = ELF32_ST_INFO(STB_LOCAL,symchain->type);
if (symchain->type == SYM_ABS)
sym->s.st_shndx = ECH(SHN_ABS);
else
sym->s.st_shndx = ECVH((uint16)symchain->relsect->index);
}
}
}
symchain = symchain->hash_chain;
}
}
firstglobal = symindex + 1;
for (i=0; i<SYMHTABSIZE; i++) { /* then, global and weak symbols */
symchain = gv->symbols[i];
while (symchain) {
if (*(symchain->name) != '.') { /* '.symbols' are ignored */
if (symchain->type==SYM_ABS || symchain->type==SYM_RELOC) {
if (symchain->bind > SYMB_LOCAL) {
++symindex;
sym = addSymbol(&symlist,&strlist,symchain->name);
sym->s.st_value = ECVW(symchain->value);
sym->s.st_size = ECVW(symchain->size);
sym->s.st_info = ELF32_ST_INFO(symchain->bind,symchain->type);
if (symchain->type == SYM_ABS)
sym->s.st_shndx = ECH(SHN_ABS);
else
sym->s.st_shndx = ECVH((uint16)symchain->relsect->index);
}
}
}
symchain = symchain->hash_chain;
}
}
/* ".rela.xxx" relocation sections */
sec = (struct Section *)gv->sectionlist.first;
while (nextsec = (struct Section *)sec->n.next) {
if (!(sec->flags & SF_DISCARD) && sec->size > 0) {
struct Reloc *nextrel,*rel=(struct Reloc *)sec->reloclist.first;
struct XReference *nextxref;
struct XReference *xref=(struct XReference *)sec->xreflist.first;
uint32 ro=roffset;
char *sptr;
while (nextrel = (struct Reloc *)rel->n.next) {
addRela(&relalist,rel->offset,rel->addend,
ELF32_R_INFO(rel->relocsect->index,rel->type));
roffset += sizeof(struct Elf32_Rela);
rel = nextrel;
}
while (nextxref = (struct XReference *)xref->n.next) {
struct SymbolNode *nextsym;
char *xname = xref->xsymbol->name;
uint32 sidx=0;
/* check if referenced symbol is already in symbol table */
sym = (struct SymbolNode *)symlist.first;
while (nextsym = (struct SymbolNode *)sym->n.next) {
if (sym->name)
if (!strcmp(xname,sym->name))
break;
++sidx;
sym = nextsym;
}
if (nextsym==NULL) {
sidx = ++symindex;
sym = addSymbol(&symlist,&strlist,xname);
sym->s.st_info = ELF32_ST_INFO(STB_GLOBAL,STT_NOTYPE);
}
addRela(&relalist,xref->offset,xref->addend,
ELF32_R_INFO(sidx,xref->type));
roffset += sizeof(struct Elf32_Rela);
xref = nextxref;
}
if (ro != roffset) { /* were there any relocations? */
sptr = (char *)alloc(strlen(sec->name) + 5);
sprintf(sptr,".rela%s",sec->name);
++shdrindex;
shn = addShdr(&shdrlist);
shn->s.sh_name = ECVW(addString(&shstrlist,sptr));
shn->s.sh_type = ECW(SHT_RELA);
shn->s.sh_offset = ro; /* relative offset, corrected later */
shn->s.sh_size = ECVW(roffset - ro);
/* sh_link will be set later, when .symtab exists */
shn->s.sh_info = ECVW(sec->index); /* shdr idx to which reloc applies */
shn->s.sh_addralign = ECW(4);
shn->s.sh_entsize = ECW(sizeof(struct Elf32_Rela));
}
}
sec = nextsec;
}
/* ".shstrtab" section header string table */
++shdrindex;
shn = addShdr(&shdrlist);
shn->s.sh_name = ECVW(shstrtabidx);
shn->s.sh_type = ECW(SHT_STRTAB);
shn->s.sh_offset = ECVW(soffset);
shn->s.sh_size = ECVW(shstrlist.index);
shn->s.sh_addralign = ECW(1);
soffset += shstrlist.index;
align1 = ((soffset + 3) & ~3) - soffset;
soffset += align1;
elf_header.e_shoff = ECVW(soffset);
soffset += (shdrindex+3)*sizeof(struct Elf32_Shdr);
elf_header.e_shstrndx = ECVH((uint16)shdrindex);
elf_header.e_shnum = ECVH((uint16)shdrindex+3);
/* ".symtab" symbol table */
++shdrindex;
++symindex; /* number of symbol in symbol table */
shn = addShdr(&shdrlist);
shn->s.sh_name = ECVW(symtabidx);
shn->s.sh_type = ECW(SHT_SYMTAB);
shn->s.sh_offset = ECVW(soffset);
shn->s.sh_size = symindex*sizeof(struct Elf32_Sym);
shn->s.sh_link = ECVW(shdrindex+1); /* associated .strtab section */
shn->s.sh_info = ECVW(firstglobal); /* first non-local symbol index */
shn->s.sh_addralign = ECW(4);
shn->s.sh_entsize = ECW(sizeof(struct Elf32_Sym));
soffset += shn->s.sh_size;
#ifdef LITTLEENDIAN
shn->s.sh_size = l2bw(shn->s.sh_size);
#endif
/* ".strtab" string table */
shn = addShdr(&shdrlist);
shn->s.sh_name = ECVW(strtabidx);
shn->s.sh_type = ECW(SHT_STRTAB);
shn->s.sh_offset = ECVW(soffset);
shn->s.sh_size = ECVW(strlist.index);
shn->s.sh_addralign = ECW(1);
soffset += strlist.index;
align2 = ((soffset + 3) & ~3) - soffset;
soffset += align2; /* offset for first Rela-entry */
/* create output file */
if (fp = fopen(output_name,"w")) {
struct StrTabNode *stn;
struct RelaNode *rn;
fw(fp,&elf_header,sizeof(struct Elf32_Ehdr)); /* write header */
/* write initialized section contents */
sec = (struct Section *)gv->sectionlist.first;
while (nextsec = (struct Section *)sec->n.next) {
if (!(sec->flags & (SF_DISCARD|SF_UNINITIALIZED)))
fw(fp,sec->contents,sec->size);
sec = nextsec;
}
/* write comment section */
if (gv->ident || gv->vc) {
fw(fp,gv->alignment_bytes,1); /* write leading 0-byte */
if (gv->ident)
fw(fp,gv->ident,strlen(gv->ident)+1);
if (gv->vc) {
uint32 v = ECW(0x16020303);
fw(fp,&v,4);
v = ECVW((uint32)time(NULL));
fw(fp,&v,4);
v = ECW(0x03030216);
fw(fp,&v,4);
}
}
/* write .shstrtab string table */
while (stn = (struct StrTabNode *)remhead(&shstrlist.l))
fw(fp,stn->str,strlen(stn->str)+1);
/* write section headers */
fw(fp,gv->alignment_bytes,align1);
while (shn = (struct ShdrNode *)remhead(&shdrlist)) {
if (shn->s.sh_type == SHT_RELA) {
shn->s.sh_offset += soffset; /* set correct offset */
#ifdef LITTLEENDIAN
shn->s.sh_offset = l2bw(shn->s.sh_offset);
#endif
shn->s.sh_link = ECVW(shdrindex); /* index of associated symbol table */
}
fw(fp,&(shn->s),sizeof(struct Elf32_Shdr));
}
/* write symbol table */
while (sym = (struct SymbolNode *)remhead(&symlist))
fw(fp,&(sym->s),sizeof(struct Elf32_Sym));
/* write .strtab string table */
while (stn = (struct StrTabNode *)remhead(&strlist.l))
fw(fp,stn->str,strlen(stn->str)+1);
/* write relocations */
fw(fp,gv->alignment_bytes,align2);
while (rn = (struct RelaNode *)remhead(&relalist))
fw(fp,&(rn->r),sizeof(struct Elf32_Rela));
fclose(fp);
}
else
error(25,output_name); /* unable to create output file */
}
static struct ShdrNode *addShdr(struct list *l)
{
struct ShdrNode *s = alloczero(sizeof(struct ShdrNode));
addtail(l,&(s->n));
return (s);
}
static struct SymbolNode *addSymbol(struct list *l,struct StrTabList *sl,
char *name)
{
struct SymbolNode *sn = alloczero(sizeof(struct SymbolNode));
addtail(l,&(sn->n));
if (name) {
sn->name = name;
sn->s.st_name = ECVW(addString(sl,name));
}
return (sn);
}
static uint32 addString(struct StrTabList *sl,char *s)
{
struct StrTabNode *sn = alloc(sizeof(struct StrTabNode));
uint32 idx = sl->index;
sn->str = s;
addtail(&(sl->l),&(sn->n));
sl->index += (uint32)strlen(s) + 1;
return (idx);
}
static void addRela(struct list *l,uint32 o,uint32 a,uint32 i)
{
struct RelaNode *rn = alloc(sizeof(struct RelaNode));
rn->r.r_offset = ECVW(o);
rn->r.r_addend = ECVW(a);
rn->r.r_info = ECVW(i);
addtail(l,&(rn->n));
}
static void fw(FILE *fp,void *buf,size_t len)
{
if (len) {
if (!fwrite(buf,1,len,fp)) {
fclose(fp);
error(26,output_name); /* write error */
}
}
}